const std = @import("std");

const CodeWriter = @import("CodeWriter.zig");
const Command = @import("Command.zig");

pub fn main() anyerror!void {
    var gpa: std.heap.GeneralPurposeAllocator(.{}) = .{};
    defer std.debug.assert(gpa.deinit() == .ok);
    const allocator = gpa.allocator();

    const args = try std.process.argsAlloc(allocator);
    defer std.process.argsFree(allocator, args);

    if (args.len < 2) {
        std.log.err("Usage: {s} <file.vm>", .{args[0]});
        return;
    }

    // open vm file
    const cwd = std.fs.cwd();
    const vmfile = try cwd.openFile(args[1], .{});
    defer vmfile.close();

    // truncate asm file for writing
    const vmfilename = args[1];
    const noextension = vmfilename[0 .. vmfilename.len - 2]; // file name without extension "vm"
    const asmfilename = try std.mem.concat(allocator, u8, &.{ noextension, "asm" });
    defer allocator.free(asmfilename);

    const asmfile = try cwd.createFile(asmfilename, .{ .truncate = true });
    defer asmfile.close();

    // translate
    const reader = vmfile.reader();
    const writer = asmfile.writer();

    const linebuf = try allocator.alloc(u8, 1024);
    defer allocator.free(linebuf);

    var basename = std.fs.path.basename(vmfilename);
    basename = basename[0 .. basename.len - 3]; // file name without extension ".vm"

    var cw: CodeWriter = .{ .writer = writer };

    while (try getline(reader, linebuf)) |line| {
        if (line.len == 0) continue; // skip empty lines
        const command: ?Command = try .parse(line);
        if (command) |cmd| {
            try writer.print("//{s}\n", .{line});

            switch (cmd.type) {
                .push, .pop => {
                    try cw.pushpop(cmd, basename);
                },
                .arithmetic => {
                    try cw.arithmetic(cmd.arg1);
                },
                else => {},
            }
        }
    }
}

pub fn getline(reader: std.fs.File.Reader, buffer: []u8) !?[]const u8 {
    // 显式指定变量类型为 []const u8，把 []u8 赋值给它
    // 因为下面的 trimRight 返回 []const u8，不能把 []const u8 赋值给 []u8
    const line: []const u8 = (try reader.readUntilDelimiterOrEof(buffer, '\n')) orelse return null;

    // trim annoying windows-only carriage return character
    return std.mem.trimRight(u8, line, "\r");
}
